using System;
using System.Collections;
using System.IO;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.CDS;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.Policy;
using PHSRAG.Security;
using PHSRAG.Utility;
using System.Data.SqlClient;
using System.Data;

namespace PHSRAG.Insight21.Humans
{
    /// <summary>
    /// Displays all the versions of a ProtocolDocument, allows to add/replace a version, and find differences between 2 word documents
    /// </summary>
    public partial class AttachmentVersionsPage : BasePage
    {
        #region Nested type: GridColumns

        /// <summary>
        /// List of all columns in the grid
        /// </summary>
        private enum GridColumns
        {
            ProtocolDocId = 0,
            DocumentId,
            SelectFiles,
            AttachmentMode,
            TitleName,
            DateUploaded,
            ViewAttachment
        }

        #endregion

        #region Member Fields

        /// <summary>
        /// Holds the ProtocolDocument object from the Attachments form on which the users clicks to view its versions
        /// </summary>
        private ProtocolDocument attachment;

        private ICachePolicy cachePolicy;

        /// <summary>
        /// Keeps a track of the number of files with a .doc extension
        /// </summary>
        private int countDocFiles;

        /// <summary>
        /// Holds the error message that needs to be shown to the user in the annunciator
        /// </summary>
        private StringBuilder errorReport = new StringBuilder();

        private ICachePolicy pageCachePolicy;

        /// <summary>
        /// Holds all the ProtocolDocument objects that constitute different versions of an attachment
        /// </summary>
        private ArrayList protocolDocuments;

        private ProtocolProcess protocolProcess;

        #endregion Member Fields

        #region Constants

        /// <summary>
        /// The key for the caching mechanism that holds the ProtocolDocument objects that represent all the existing versions of an attachment
        /// </summary>
        private const string AttachmentVersionsDataKey = "AttachmentVersionsDataKey_E1D823D3-C0FD-4535-B05F-74D0BBFB686A";

        private const string CurrentProcessKey = "currentProcess_0836847B-533E-4f05-9C24-7B170B61B99E";

        /// <summary>
        /// The key for the caching mechanism that holds the ProtocolDocument selected in the Attachments page
        /// </summary>
        private const string CurrentProtocolDocumentKey = "CurrentProtocolDocumentKey_5EE774C3-6FA3-4aa0-9507-06630C8466FF";

        private const string FileCompErrMsg = "Failed to find out differences between the 2 files";

        private const string FileIdenticalMsg = "The two files are identical.";
        private const string FileProtectedMsg = "The files are protected and can not be compared.";

        /// <summary> file identical
        /// Return message to user that the two files are identical
        /// </summary>
        private const string IdenticalFile = "FileIdentical";

        private const string ProtectedFile = "FileProtected";
        private const string ScanAttachmentsForVirusesConfigKey = "ScanAttachmentsForViruses";
        private int Id = 0;

        /// <summary>
        /// The configuration key that holds the name of the staging folder
        /// </summary>
        private const string StagingFolderConfigKey = "StagingFolder";

        #endregion

        #region Events (Page)

        protected void Page_Load(object sender, EventArgs e)
        {
            attachmentElectronic.Attributes["onclick"] = "ToggleControlsForVersionsElectronicFile('attachmentElectronic', 'attachmentHardCopy','attachedFile','newTitle')";
            attachmentHardCopy.Attributes["onclick"] = "ToggleControlsForVersionsHardCopy('attachmentHardCopy', 'attachmentElectronic','attachedFile','newTitle')";

            annunciator.Message = string.Empty;

            

            if (!IsPostBack && postbackGuard.IsValid)
            {
                //Retrieve the document versions for the attachment selected in Attachments page and store the arraylist in cache
                Hashtable incomingData = (Hashtable) pageCachePolicy.GetCachedObject(Keys.IncomingDataKey);
                if (incomingData != null)
                {
                    attachment = (ProtocolDocument) incomingData[Keys.ProtocolDocumentKey]; //The document whose versions are being displayed
                    protocolProcess = (ProtocolProcess) incomingData[Keys.ProtocolProcessKey];
                    int userAccessLevel = Convert.ToInt32(incomingData[Keys.ProtocolAccessKey]);
                    pageCachePolicy.Cache(CurrentProtocolDocumentKey, attachment);
                    pageCachePolicy.Cache(CurrentProcessKey, protocolProcess);
                    pageCachePolicy.UnCache(Keys.IncomingDataKey);

                    if (protocolProcess == null || protocolProcess.Locked ||
                        !((userAccessLevel & protocolProcess.Protocol.ManagePermission) > 0 ||
                            (userAccessLevel & protocolProcess.Protocol.EditPermission) > 0))
                    {
                        addReplaceVersion.Visible = false;
                    }

                    protocolDocuments = attachment.GetDocumentVersions(DataAccess.ConnectionString,
                        ((protocolProcess != null) ? protocolProcess.Id : 0), pageCachePolicy);

                    protocolDocuments.Insert(0, attachment); //The latest version is also listed out in this page
                    pageCachePolicy.Cache(AttachmentVersionsDataKey, protocolDocuments);
                    GridBind();
                }
            }
            else
            {
                protocolProcess = (ProtocolProcess) pageCachePolicy.GetCachedObject(CurrentProcessKey);
                protocolDocuments = (ArrayList) pageCachePolicy.GetCachedObject(AttachmentVersionsDataKey);
                attachment = (ProtocolDocument) protocolDocuments[0];
                if (protocolDocuments == null)
                {
                    //Session timed out
                    Response.ClearContent();
                    Response.Write("Session time out");
                    return;
                }
            }
            attachmentTitle.Text = attachment.Name;
            attachmentType.Text = attachment.RefMeaning;
        }

        protected void Page_PreRender(object sender, EventArgs e)
        {
            //The Show Differences button is made visible only if there are 2 or more versions which are .doc files
            showDifferences.Visible = countDocFiles >= 2;
        }
        #endregion

        #region Web Form Designer generated code

        protected override void OnInit(EventArgs e)
        {           
            InitializeComponent();
            base.OnInit(e);

            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];
        }

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() { }
        #endregion

        #region Events (controls)
        protected void OnVersionsDetailItemDataBound(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                ProtocolDocument currentPD = (ProtocolDocument) protocolDocuments[e.Item.ItemIndex];

                Label attachmentMode = (Label) e.Item.FindControl("attachmentMode");
                Label attachmentDateUploaded = (Label) e.Item.FindControl("attachmentDateUploaded");
                Label titleName = (Label)e.Item.FindControl("titleName");
                ImageButton viewAttachment = (ImageButton) e.Item.FindControl("viewAttachment");
                CheckBox fileToDiff = (CheckBox) e.Item.FindControl("fileToDiff");
                CheckBox attachmentMarkForDelete = (CheckBox) e.Item.FindControl("attachmentMarkForDelete");

                if (currentPD.IsElectronicFile)
                {
                    attachmentMode.Text = "Electronic";
                }
                else if (currentPD.IsHardCopy)
                {
                    attachmentMode.Text = "Hardcopy";
                }

                //check if the DateTime value is set to MinValue. If so, dont display the date
                DateTime dateUploaded = ((ProtocolDocument) protocolDocuments[e.Item.ItemIndex]).DateReceived;
                attachmentDateUploaded.Text = dateUploaded == DateTime.MinValue ? "" : dateUploaded.ToShortDateString();

                titleName.Text = ((ProtocolDocument)protocolDocuments[e.Item.ItemIndex]).Name.ToString();
                //Enable the checkbox against each row only if the file has a .doc extension
                //Enabled checkboxes are tied to a javascript call which lets the user select only 2 rows in the grid
                String path = ((ProtocolDocument) protocolDocuments[e.Item.ItemIndex]).FileName;
                fileToDiff.Enabled = false;
                if (!string.IsNullOrEmpty(path))
                {
                    viewAttachment.Visible = true;
                    viewAttachment.ImageUrl = SetIcon(path);
                    //Check box for selecting files
                    string ext = Path.GetExtension(path).ToLower();
                    if (ext == ".doc")
                    {
                        fileToDiff.Enabled = true;
                        fileToDiff.Attributes.Add("OnClick",
                            "javascript:RestrictFileSelection('" + fileToDiff.ClientID + "', '" + showDifferences.ClientID + "')");
                    }
                }

                if (!currentPD.IsReplaceable)
                {
                    attachmentMarkForDelete.Enabled = false;
                }
            }
        }

        protected void OnVersionsDetailItemCommand(object source, DataGridCommandEventArgs e)
        {
            //Cache the ProtocolDocument corresponding to the link on which the user clicks. 
            //Register a script that will pop up a new window and loads the downloadform.aspx
            //The downloadform displays the file represented by the cached protocolDocument
            Hashtable outgoingData = new Hashtable();
            Document document = ((ProtocolDocument) protocolDocuments[e.Item.ItemIndex]).ElectronicFile;
            outgoingData[Keys.DocumentKey] = document;

            cachePolicy.Cache(Keys.IncomingDataKey, outgoingData);

            if (e.CommandName == "ImageClick")
            {
                //Script for popup window
                string script = "";
                script += "\n<script language=JavaScript id='DisplayAttachment'>\n";
                script += "var confirmWin = null; \n";
                script += "confirmWin = window.open('DownloadForm.aspx?Report=attachments&DocId=" + document.Id + "', 'ViewFile', 'width=800, height=600, toolbar=no'); \n";
                script += "</script>";

                //Check whether the script is already registered
                if (!ClientScript.IsClientScriptBlockRegistered("DisplayAttachment"))
                {
                    //Register the script
                    ClientScript.RegisterClientScriptBlock(GetType(), "DisplayAttachment", script);
                }
            }
        }

        protected void OnAddReplaceVersionClick(object sender, ImageClickEventArgs e)
        {
            addNewVersionSection.Visible = true;
            GridBind();
        }

        protected void OnDeleteVersion(object sender, ImageClickEventArgs e)
        {
            try
            {
                bool isDeleted = false;
                ArrayList deletedPDs = new ArrayList();
                for (int i = 0, j = versionsDetail.Items.Count; i < j; ++i)
                {
                    ProtocolDocument protocolDoc = (ProtocolDocument) protocolDocuments[i];
                    CheckBox attachmentMarkForDelete = (CheckBox) versionsDetail.Items[i].FindControl("attachmentMarkForDelete");
                    if (attachmentMarkForDelete.Checked)
                    {
                        isDeleted = true;
                        protocolDoc.MarkForDeletion = true;
                        ((ProtocolDocument) protocolDocuments[i]).Save(DataAccess.ConnectionString);
                        deletedPDs.Add(protocolDoc);
                    }
                }

                //remove the attachment object from process docs collection and add the first object in protocoldocuments
                //to process docs collection.
                if (isDeleted)
                {
                    foreach (ProtocolDocument pd in deletedPDs)
                    {
                        protocolDocuments.Remove(pd);
                    }
                    protocolProcess.ProtocolDocuments.Remove(attachment);
                    if (protocolDocuments.Count > 0)
                    {
                        protocolProcess.ProtocolDocuments.Add(protocolDocuments[0]);
                    }
                    cachePolicy.Cache(Keys.NewVersionUploadedKey, true);
                    GridBind();
                }
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("AttachmentVersions.OnDeleteVersion(): Failed to delete the selected versions.", ex, true, ExceptionReport.ReportType.Error);
                errorReport.Append("Failed to delete the selected versions.");
            }
        }

        protected void OnUploadVersionClick(object sender, ImageClickEventArgs e)
        {
            try
            {
                if (!postbackGuard.IsValid)
                {
                    return;
                }
                
                ValidateAttachment();

                if (Id == 0)
                {
                    annunciator.Visible = false;

                    string nameOfAttachment = (newTitle.Text == String.Empty) ? attachment.Name : newTitle.Text;

                    ProtocolDocument newAttachmentType = protocolProcess.AddProtocolDocument(protocolProcess.ProtocolId, nameOfAttachment,
                        attachment.Type, attachment.RefMeaning, attachmentHardCopy.Checked, protocolProcess.Id, attachment.Id);

                    if (attachmentElectronic.Checked)
                        ProcessUploadedFile(attachedFile.PostedFile, ref newAttachmentType);
                    else
                        newAttachmentType.DateReceived = DateTime.MinValue;

                    if (newAttachmentType != null)
                    {
                        protocolProcess.ProtocolDocuments.Remove(attachment);
                        protocolDocuments.Insert(0, newAttachmentType);
                        attachment = newAttachmentType;
                    }
                    pageCachePolicy.Cache(CurrentProtocolDocumentKey, attachment);

                    //display errors, if any
                    if (errorReport.ToString() != string.Empty)
                    {
                        annunciator.Message = errorReport.ToString();
                        return;
                    }
                    attachmentElectronic.Checked = false;
                    attachmentHardCopy.Checked = false;

                    addNewVersionSection.Visible = false;
                    protocolProcess.Save(DataAccess.ConnectionString);

                    cachePolicy.Cache(Keys.NewVersionUploadedKey, true);
                    //addReplaceVersion.Text = "Replace Version";
                    GridBind();
                }
                else
                {
                    annunciator.Visible = true;
                    attachmentElectronic.Checked = false;
                    attachmentHardCopy.Checked = false;
                }
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("AttachmentVersions.OnUploadVersionClick(): Failed to upload the new version.", ex, true, ExceptionReport.ReportType.Error);
                errorReport.Append("Failed to upload the new version.");
            }
        }

        #region Validation
        private int ValidateAttachment()
        {
            Id = Convert.ToInt32(DataAccess.ExecuteScalar(Utility.DataAccess.ConnectionString, "FindMatchingAttachmentName",
                            DataAccess.BuildSqlParameterArray(
                            "@protocolId", SqlDbType.Int, protocolProcess.ProtocolId,
                            "@attachmentName", SqlDbType.VarChar, newTitle.Text.Trim(),
                            "@attachmentType", SqlDbType.VarChar, attachment.Type)));

            if (Id != 0)
            {
                annunciator.Title = "Error Saving Attachment";
                annunciator.Message = "An attachment with that name already exist.";               
            }

            return Id;
        }
        #endregion

        protected void OnVersionsDetailPreRender(object sender, EventArgs e)
        {
            //The select files for comparing column is visible only if 2 or more doc files are present in the version history
            versionsDetail.Columns[int.Parse(GridColumns.SelectFiles.ToString("d"))].Visible = countDocFiles > 1;
        }

        protected void OnShowDifferencesClick(object sender, ImageClickEventArgs e)
        {
            try
            {
                CheckBox fileToDiff;
                int i, id1 = 0, id2 = 0;

                //copy the files from db to stagin folder
                for (i = 0; i < versionsDetail.Items.Count; i++)
                {
                    fileToDiff = (CheckBox) versionsDetail.Items[i].FindControl("fileToDiff");
                    if (fileToDiff.Checked)
                    {
                        id1 = ((ProtocolDocument) protocolDocuments[i]).Id;
                        break;
                    }
                }
                for (i = i + 1; i < versionsDetail.Items.Count; i++)
                {
                    fileToDiff = (CheckBox) versionsDetail.Items[i].FindControl("fileToDiff");
                    if (fileToDiff.Checked)
                    {
                        id2 = ((ProtocolDocument) protocolDocuments[i]).Id;
                        break;
                    }
                }
                GridBind();

                InsightServiceUtility.DocumentUtility docUtility = new InsightServiceUtility.DocumentUtility();

                string filePath = HttpContext.Current.Server.MachineName;
                //string filePath = "phsragapp1";
                string outputFile = docUtility.CompareDocuments(string.Format(@"\\{0}",
                    Path.Combine(filePath, Path.GetFileName(Settings.GetKeyValue("DocumentStagingFolder", string.Empty)))),
                    DataAccess.ConnectionString,
                    id1,
                    id2);

                if (outputFile == null)
                {
                    throw new Exception("Error comparing documents.");
                }

                if (outputFile == IdenticalFile)
                {
                    sendBackFileCompareMsg(FileIdenticalMsg);
                }
                else if (outputFile == ProtectedFile)
                {
                    sendBackFileCompareMsg(FileProtectedMsg);
                }
                else
                {
                    Response.Clear();
                    Response.ClearHeaders();
                    Response.AppendHeader("content-disposition", "attachment; filename=Differences.doc");
                    Response.ContentType = "Application/msword";
                    try
                    {
                        string docWebRoot = Settings.GetKeyValue("DocumentWebRoot", "/Insight/");
                        string docStagingFolder = Settings.GetKeyValue("DocumentStagingFolder", string.Empty);
                        string docPath = docWebRoot + docStagingFolder + "/" + Path.GetFileName(outputFile); //Path.Combine(docStagingFolder, Path.GetFileName(outputFile));
                        Response.TransmitFile(docPath);
                        Response.End();
                    }
                    catch (Exception ex)
                    {
                        ExceptionReport.WriteToEventLog(String.Format("Failed while transmitting the file {0} to the client.", outputFile),
                            ex, true, ExceptionReport.ReportType.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("AttachmentVersions.OnShowDifferencesClick(): Failed to find out differences between the 2 files", ex, true, ExceptionReport.ReportType.Error);
                errorReport.Append(FileCompErrMsg);
                sendBackFileCompareMsg("Error during file comparation," + FileCompErrMsg);
            }
        }

        #endregion

        #region Private methods

        private void GridBind()
        {
            versionsDetail.DataSource = protocolDocuments;
            versionsDetail.DataBind();
        }

        /// <summary>
        /// Sets the image based on the file extension
        /// </summary>
        /// <param name="fileName">Name of the file</param>
        /// <returns>ImageUrl</returns>
        private string SetIcon(string fileName)
        {
            //based on the extension set the icon
            string innerHtml;
            string ext = Path.GetExtension(fileName).ToLower();

            switch (ext)
            {
                case ".pdf":
                    innerHtml = "../Images/pdf.gif";
                    break;
                case ".doc":
                    innerHtml = "../Images/word.jpg";
                    ++countDocFiles;
                    break;
                case ".txt":
                    innerHtml = "../Images/note.gif";
                    break;
                case ".xls":
                    innerHtml = "../Images/excel.jpg";
                    break;
                case ".ppt":
                    innerHtml = "../Images/powerpoint.jpg";
                    break;
                default:
                    innerHtml = "../Images/attachment.jpg";
                    break;
            }

            return innerHtml;
        }

        /// <summary>
        /// Creates a Document object with the file posted to the server and attaches it to the protocolDocument
        /// </summary>
        /// <param name="postedFile">Control that holds the uploaded file details</param>
        /// <param name="protocolDoc">The ProtocolDocument to which the Document object should be linked</param>
        private void ProcessUploadedFile(HttpPostedFile postedFile, ref ProtocolDocument protocolDoc)
        {
            if (postedFile != null)
            {
                try
                {
                    string fileName = Path.GetFileName(postedFile.FileName);
                    if (postedFile.ContentLength > 0)
                    {
                        //store uploaded files in a temporary location, virus scan & upload it to database & set the documentId column of the datatable
                        string filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
                        postedFile.SaveAs(filePath);

                        bool isClean = true;
                        if (bool.Parse(Settings.GetKeyValue("VirusScanner.Enable", "false")))
                        {
                            try
                            {
                                Document.ScanFileForVirus(filePath);
                            }
                            catch (Exception ex)
                            {
                                isClean = false;
                                errorReport.Append(string.Format("Virus scan on {0} failed.\r\nFailure Details: {1}", fileName, ex.Message));
                            }
                        }
                        if (isClean)
                        {
                            string stagingFolder = Settings.GetKeyValue("StagingFolder", string.Empty);
                            UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;
                            string attachmentNamePrefixForStaging = string.Format("{0}_{1}_{2}", userContext.Username, protocolProcess.ProtocolId, protocolProcess.Id);
                            string newFileName = string.Format("{0}_{1}_{2}_{3}", attachmentNamePrefixForStaging, attachmentType.Text, Helper.GetFileSystemSupportedName(attachmentTitle.Text), new FileInfo(postedFile.FileName).Name);
                            newFileName = newFileName.Replace('/', '_');
                            newFileName = newFileName.Replace('\\', '_');

                            try
                            {
                                WindowsImpersonationContext wic = Authenticator.ImpersonateUser(
                                    Settings.GetKeyValue("ServiceAccount", "RAG29"),
                                    Settings.GetKeyValue("SAPassword", "z0hcq"),
                                    Authenticator.LogonType.NewCredentials, Authenticator.LogonProvider.WinNT50);
                                string newFilePath = Path.Combine(stagingFolder, newFileName);
                                if (File.Exists(newFilePath))
                                {
                                    File.Delete(newFilePath);
                                }
                                File.Move(filePath, newFilePath);
                                Authenticator.RevertUser(wic);
                            }
                            catch (Exception ex)
                            {
                                ExceptionReport.WriteToEventLog("Insight", string.Format("Failed while impersonating and saving the file to a network drive: {0}", ex), ExceptionReport.ReportType.Error);
                                throw new Exception(string.Format("Failed while impersonating and saving the file to a network drive: {0}", ex));
                            }

                            InsightServiceUtility.DocumentUtility docUtility = new InsightServiceUtility.DocumentUtility();

                            int docId;
                            Protocol protocol = new Protocol(cachePolicy);
                            protocol = (Protocol)cachePolicy.GetCachedObject(protocol.CacheKey());
                            string folderHierarchy = ProtocolDocument.CreateFolderHierarchy(protocol, protocolDoc.Id, fileName);
                            string[] metaData = new string[]
                            {
                                "ProcessName", protocolDoc.ProcessName,
                                "Att_Type", protocolDoc.RefMeaning,
                                "Att_Title", protocolDoc.Name,
                                "DocName", fileName
                            };
                            if (Utility.Settings.GetKeyValue("SaveToSharePoint", false))
                            {
                                docId = docUtility.SaveFileToSharepoint(Utility.DataAccess.ConnectionString, newFileName, fileName, metaData, folderHierarchy);
                            }
                            else
                            {
                                docId = docUtility.SaveFileToDatabase(Utility.DataAccess.ConnectionString, newFileName, fileName);
                            }

                            if (docId == -1)
                            {
                                throw new Exception("Web service returned an error");
                            }

                            Document doc = new Document(cachePolicy);
                            doc.Load(DataAccess.ConnectionString, docId);
                            protocolDoc.ElectronicFile = doc;
                            protocolDoc.DateReceived = DateTime.Now;                            
                            protocolDoc.Save(DataAccess.ConnectionString);
                        }
                        else
                        {
                            throw new Exception();
                        }
                    }
                    else
                    {
                        errorReport.Append(fileName + " is empty or invalid;" + "\r\n");
                        throw new Exception();
                    }
                }
                catch (Exception ex)
                {
                    //remove the protocoldocument, from the collection
                    protocolProcess.ProtocolDocuments.RemoveAt(protocolProcess.ProtocolDocuments.Count - 1);
                    protocolDoc = null;

                    ExceptionReport.WriteToEventLog("AttachmentVersions.ProcessUploadedFile(): Failed to process the uploaded file", ex, true, ExceptionReport.ReportType.Error);
                    errorReport.Append("Failed to process the uploaded file");
                }
            }
        }

        /// <summary> send file compare msg back to user
        /// send file compare msg back to user
        /// </summary>
        /// <param name="msg">msg of file comparation result </param>
        private void sendBackFileCompareMsg(string msg)
        {
            ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
            ScriptManager.RegisterClientScriptBlock(showDifferences, GetType(), "fileCompMsg", "<script language='JavaScript'>alert('" + msg + "');</script>", false);
        }

        #endregion
    }
}